package com.nofeng.wx.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.nofeng.wx.controller.stock.view.*;
import com.nofeng.wx.service.stock.AnalysisContext;
import com.nofeng.wx.service.stock.BacktestResult;
import com.nofeng.wx.service.stock.DailyStockData;
import com.nofeng.wx.service.stock.StockInfo;
import com.nofeng.wx.third.EastMoneyStockApi;
import com.nofeng.wx.third.FinanceHttpApi;
import com.nofeng.wx.third.NocodeDbApi;
import com.nofeng.wx.utils.BaseUtils;
import com.nofeng.wx.utils.GuavaCacheUtils;
import com.nofeng.wx.utils.IndicatrixUtils;
import com.nofeng.wx.utils.NoHttpsClientUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.nofeng.wx.service.stock.StrategyBacktester.runBacktest;

@Slf4j
@Service
public class StockService {

    @Autowired
    private NocodeDbApi nocodeDbApi;

    ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(20));

    @Autowired
    FinanceHttpApi financeHttpApi;

    public Long bft() throws Exception {
        BigDecimal bft = BigDecimal.valueOf(0);
        List<FinanceHttpApi.HttpStockData> stockDayList = financeHttpApi.getNow("s_sh000001,s_sz399006,s_sz399001");
        BigDecimal v = BigDecimal.valueOf(0);
        for (FinanceHttpApi.HttpStockData stockDay : stockDayList) {
            v = v.add(stockDay.getVolumeMoney());
        }
        bft = v.divide(BigDecimal.valueOf(1260000), 2, RoundingMode.DOWN);
        bft = bft.multiply(BigDecimal.valueOf(100));
        return bft.longValue();
    }

    /**
     * 上证指数
     * @return
     * @throws Exception
     */
    public DailyStockData sz() throws Exception {
        List<FinanceHttpApi.HttpStockData> sSh000001 = financeHttpApi.getNow("s_sh000001");
        List<DailyStockData> resultList = sSh000001.stream().map(this::convertStockDay).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(resultList)) {
            return resultList.get(0);
        }
        return null;
    }

    /**
     * 深证综指
     * @return
     * @throws Exception
     */
    public DailyStockData ssz() throws Exception {
        List<FinanceHttpApi.HttpStockData> sSh000001 = financeHttpApi.getNow("s_sz399106");
        List<DailyStockData> resultList = sSh000001.stream().map(this::convertStockDay).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(resultList)) {
            return resultList.get(0);
        }
        return null;
    }
    public DailyStockData cy() throws Exception {
        List<FinanceHttpApi.HttpStockData> sSh000001 = financeHttpApi.getNow("s_sz399006");
        List<DailyStockData> resultList = sSh000001.stream().map(this::convertStockDay).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(resultList)) {
            return resultList.get(0);
        }
        return null;
    }

    public DailyStockData kc() throws Exception {
        List<FinanceHttpApi.HttpStockData> sSh000001 = financeHttpApi.getNow("s_sh000680");
        List<DailyStockData> resultList = sSh000001.stream().map(this::convertStockDay).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(resultList)) {
            return resultList.get(0);
        }
        return null;
    }

    public FinanceHttpApi.HttpStockData getUSDCNH() throws Exception {
        FinanceHttpApi.HttpStockData usdcnh = financeHttpApi.getUSDCNH();
        return usdcnh;
    }


    public List<DailyStockData> getByCode(String code) throws Exception {
        String localCacheKey = "getByCode_" + code;
        String localCacheValue = GuavaCacheUtils.get(localCacheKey);
        if (StringUtils.isNotEmpty(localCacheValue)) {
            return JSONObject.parseArray(localCacheValue, DailyStockData.class);
        }


//        List<FinanceHttpApi.HttpStockData> httpStockDataList = financeHttpApi.get(code, FinanceHttpApi.DATA_TYPE_DAY);
        List<FinanceHttpApi.HttpStockData> httpStockDataList = financeHttpApi.getMinute(code);
        List<FinanceHttpApi.HttpStockData> now = FinanceHttpApi.getNow(code);

        boolean hadNow = httpStockDataList.stream().anyMatch(n -> n.getCurDate().equals(BaseUtils.getDate(0).toString()));
        if (!hadNow) {
            FinanceHttpApi.HttpStockData nowData = now.stream().filter(n -> n.getCurDate().equals(BaseUtils.getDate(0).toString())).findFirst().orElseGet(null);
            if (null != nowData) {
                nowData.setStockCode(code);
                httpStockDataList.add(nowData);
            }
        }
        List<DailyStockData> resultList = httpStockDataList.stream().map(this::convertStockDay).collect(Collectors.toList());

        List<BigDecimal> closeList = CollectionUtils.emptyIfNull(resultList).stream().map(DailyStockData::getClose).collect(Collectors.toList());

        double[] closeArg = new double[closeList.size()];
        double[] rsi1 = new double[closeList.size()];
        double[] rsi2 = new double[closeList.size()];
        double[] rsi3 = new double[closeList.size()];

        for (int i = 0; i < closeList.size(); i++) {
            closeArg[i] = closeList.get(i).doubleValue();
        }
        IndicatrixUtils.rsi(closeArg, 4, 7, 21, rsi1, rsi2, rsi3);

        double[] ma5 = IndicatrixUtils.ma(4, closeArg);
        double[] ma10 = IndicatrixUtils.ma(7, closeArg);
        double[] ma20 = IndicatrixUtils.ma(21, closeArg);

        for (int i = 0; i < resultList.size(); i++) {
            DailyStockData d = resultList.get(i);
//            if (rsi1[i] <= 0) {
////                continue;
//            }

            d.setMa5(BigDecimal.valueOf(ma5[i]));
            d.setMa10(BigDecimal.valueOf(ma10[i]));
            d.setMa20(BigDecimal.valueOf(ma20[i]));
            d.setRsi1(BigDecimal.valueOf(rsi1[i]));
            d.setRsi2(BigDecimal.valueOf(rsi2[i]));
            d.setRsi3(BigDecimal.valueOf(rsi3[i]));
        }
        GuavaCacheUtils.put(localCacheKey, JSONObject.toJSONString(resultList));
        return resultList;
    }

    public List<DailyStockData> sortRsi(List<String> codeList, int sec) throws Exception {
        List<DailyStockData> dataList = Lists.newArrayList();
        List<ListenableFuture<DailyStockData>> futures = new ArrayList<>();
        CountDownLatch countDownLatch = new CountDownLatch(codeList.size());
        for (String code : codeList) {
            ListenableFuture<DailyStockData> futureCurData = service.submit(() -> {
                try {
//                   StockReportRsiView rsiView = analysisRsi(code);
                    StockReportMinuteRsiView rsiView = analysisMinuteRsi(code, sec);
                    List<DailyStockData> byCodeList = getByCode(code);
                    DailyStockData cur = byCodeList.get(byCodeList.size() - 1);
                    cur.setIsBuy(0);
                    double near10 = rsiView.getNear10MinList().get(rsiView.getDayList().size() - 1).doubleValue();
                    double ma20 = rsiView.getMa20List().get(rsiView.getDayList().size() - 1).doubleValue();
                    double highMore = rsiView.getMa20List().get(rsiView.getHighMoreList().size() - 1).doubleValue();
                    double ma20Max = rsiView.getClose20MaxList().get(rsiView.getDayList().size() - 1).doubleValue();

                    ArrayList<BigDecimal> cutList = Lists.newArrayList();
                    if (ma20 < cur.getMoney().doubleValue()) {
                        cutList.add(new BigDecimal(ma20));
                    }

                    cutList.add(new BigDecimal(highMore));
                    if (CollectionUtils.isNotEmpty(cutList)) {
                        BigDecimal cutePrice = IndicatrixUtils.max(cutList);
                        cur.setAutoCutePrice(
                                cutePrice
                        );
                        cur.setCuteLv(1);
                    }

                    if (CollectionUtils.isNotEmpty(rsiView.getBuyPointViewList())) {
                        ChartPointView chartPointView = rsiView.getBuyPointViewList().get(rsiView.getBuyPointViewList().size() - 1);
                        if ("BUYNOW".equalsIgnoreCase(chartPointView.getValue())) {
                            cur.setIsBuy(1);
                            cur.setDesc(chartPointView.getXAxis());
                        }
                        if ("SELLNOW".equalsIgnoreCase(chartPointView.getValue())) {
                            cur.setIsSell(1);
                            cur.setDesc(chartPointView.getXAxis());
                        }
                    }

                    /*
                    List<List<ChartPointView>> buyPointViewList = rsiView.getBuyPointViewList();
                    if (CollectionUtils.isNotEmpty(buyPointViewList)) {
                        List<ChartPointView> lasterBuyPointList = buyPointViewList.get(buyPointViewList.size() - 1);
                        boolean isBuy = CollectionUtils.isNotEmpty(buyPointViewList) &&
                                lasterBuyPointList.get(0).getXAxis().equals(cur.getCurDate().toString());

                        if (isBuy) {
                            cur.setIsBuy(1);
                        }

                        ChartPointView lastSell = lasterBuyPointList.get(lasterBuyPointList.size() - 1);
                        boolean isSell = CollectionUtils.isNotEmpty(buyPointViewList)
                                && lastSell.getXAxis().equals(cur.getCurDate().toString())
                                && StringUtils.equals("sell", lastSell.getName());

                        if (isSell) {
                            cur.setIsSell(1);
                        }
                    }


                    cur.setDesc(rsiView.getDesc());
                    cur.setBuyRangeDesc(rsiView.getBuyRangeDesc());
                    */
                    cur.setDesc("");
                    cur.setBuyRangeDesc("");
                    countDownLatch.countDown();
                    return cur;
                } catch (Exception e) {
                    log.error("error", e);
                }
                countDownLatch.countDown();
                return null;
            });
            futures.add(futureCurData);
        }
        countDownLatch.await();
        for (ListenableFuture<DailyStockData> future : futures) {
            DailyStockData stockData = future.get();
            if (null != stockData) {
                dataList.add(stockData);
            }
        }


        dataList.sort(Comparator.comparing(DailyStockData::getRsi1));

        dataList = initName(dataList);
        return dataList;
    }

    public List<DailyStockData> initName(List<DailyStockData> dataList) throws Exception {
        List<String> codeList = dataList.stream().filter(d -> StringUtils.isEmpty(d.getStockName())).map(DailyStockData::getStockCode).collect(Collectors.toList());
        for (String code : codeList) {
            for (DailyStockData dailyStockData : dataList) {
                if (dailyStockData.getStockCode().equals(code)) {
                    //优先从缓存读取
                    String localCacheKey = "name_" + code;
                    String localCacheValue = GuavaCacheUtils.getLocal(localCacheKey);
                    if (StringUtils.isNotEmpty(localCacheValue)) {
                        dailyStockData.setStockName(localCacheValue);
                        continue;
                    }
                    List<FinanceHttpApi.HttpStockData> httpStockDataList = financeHttpApi.getNow(code);
                    if (CollectionUtils.isNotEmpty(httpStockDataList)) {
                        dailyStockData.setStockName(httpStockDataList.get(0).getStockName());
                        GuavaCacheUtils.putLocal(localCacheKey, httpStockDataList.get(0).getStockName());
                    }
                }
            }
        }

        return dataList;
    }

    public OnHandView onHand(List<String> codeList, int sec) throws Exception {
        OnHandView onHandView = new OnHandView();
        TodayView todayView = todayView();
        onHandView.setTodayView(todayView);
        onHandView.setOnHandDetailViewList(Lists.newArrayList());

        OnHandView.OnHandDetailView s300 = onHandDetail(todayView, 0.6, "510300", sec);
        onHandView.getOnHandDetailViewList().add(s300);
        if (CollectionUtils.isNotEmpty(codeList)) {
            for (String c : codeList) {
                OnHandView.OnHandDetailView codeView = onHandDetail(todayView, 0.05, c, sec);
                onHandView.getOnHandDetailViewList().add(codeView);
            }
        }

        if (CollectionUtils.isEmpty(codeList)) {

            OnHandView.OnHandDetailView scy = onHandDetail(todayView, 0.05, "159915", sec);
            onHandView.getOnHandDetailViewList().add(scy);

            Thread.sleep(1000);

            OnHandView.OnHandDetailView sxp = onHandDetail(todayView, 0.05, "512760", sec);
            onHandView.getOnHandDetailViewList().add(sxp);

            Thread.sleep(1000);

            OnHandView.OnHandDetailView syjs = onHandDetail(todayView, 0.05, "516510", sec);
            onHandView.getOnHandDetailViewList().add(syjs);

            Thread.sleep(1000);

            OnHandView.OnHandDetailView sjj = onHandDetail(todayView, 0.05, "516970", sec);
            onHandView.getOnHandDetailViewList().add(sjj);

            Thread.sleep(1000);

            OnHandView.OnHandDetailView sny = onHandDetail(todayView, 0.05, "515030", sec);
            onHandView.getOnHandDetailViewList().add(sny);

            Thread.sleep(1000);

            OnHandView.OnHandDetailView zetf = onHandDetail(todayView, 0.1, "511520", sec);
            onHandView.getOnHandDetailViewList().add(zetf);

            Thread.sleep(1000);

            OnHandView.OnHandDetailView smetf = onHandDetail(todayView, 0.05, "159867", sec);
            onHandView.getOnHandDetailViewList().add(smetf);

            Thread.sleep(1000);
            OnHandView.OnHandDetailView ys = onHandDetail(todayView, 0.05, "159652", sec);
            onHandView.getOnHandDetailViewList().add(ys);

            Thread.sleep(1000);
        }

        onHandView.getOnHandDetailViewList().sort(Comparator.comparing(c -> c.getStockData().getRsi1()));

        return onHandView;
    }

    private OnHandView.OnHandDetailView onHandDetail(TodayView todayView, double p, String code, int sec) throws Exception {
        List<DailyStockData> s300List = sortRsi(Lists.newArrayList(code), sec);
        OnHandView.OnHandDetailView s300v = new OnHandView.OnHandDetailView();
        if (CollectionUtils.isNotEmpty(s300List)) {
            DailyStockData s300 = s300List.get(s300List.size() - 1);
            s300.setRsi1(new BigDecimal(BaseUtils.format(s300.getRsi1().doubleValue())));
            s300v.setStockData(s300);
            s300v.setOnHandP((int) (p * 100));
            s300v.setOnHandMoney(new BigDecimal(BaseUtils.format(p * todayView.getExpctOnHandMoney().doubleValue())));
            s300v.setAutoCutePrice(s300.getAutoCutePrice());
            double autoCuteP = (s300.getAutoCutePrice().doubleValue() - s300.getMoney().doubleValue()) / s300.getMoney().doubleValue() * 100;
            s300v.setAutoCuteP(new BigDecimal(BaseUtils.format(autoCuteP)));
            String desc = "";
            if (null != s300.getCuteLv() && s300.getCuteLv().equals(1) && autoCuteP < 0) {
                desc = "半仓止损";
            }
            if (null != s300.getCuteLv() && s300.getCuteLv().equals(2) && autoCuteP < 0) {
                desc = "全仓止损";
            }

            if (autoCuteP > 0) {
                desc = "等待回升";
            }
            if (autoCuteP == 0) {
                desc = "破位";
            }
            s300v.setDesc(desc);
        }

        return s300v;
    }

    private TodayView todayView() {
        TodayView todayView = new TodayView();
        try {
            todayView.setSz(sz());
            todayView.setBtf(bft());
            Integer expectOnHandP = (110 - todayView.getBtf().intValue()) * 2;
            todayView.setExpectOnHandP(expectOnHandP);
            todayView.setExpctOnHandMoney(BigDecimal.valueOf(expectOnHandP * 0.01 * 100));
//            FinanceHttpApi.HttpStockData usdcnh = stockService.getUSDCNH();
//            if(null!=usdcnh){
//                todayView.setUsdcnh(usdcnh.getClose());
//                todayView.setUsdcnhPreClose(usdcnh.getOpen());
//            }

        } catch (Exception e) {
            log.error("error", e);
        }
        return todayView;
    }

    public TodayResultView toDayResult(String code){
        String codeSet = StringUtils.isNotEmpty(code)?code:"510300,159941,159915,512760,516510,518880,513090,688608";
        double totalReturn=0.00;
        List<BacktestResult> list = Lists.newArrayList();
        TodayResultView todayResultView=new TodayResultView();
        for (String c : StringUtils.split(codeSet, ",")) {
            String jsonStr = stock(c,false);
            BacktestResult result = runBacktest(jsonStr);
            list.add(result);
            totalReturn=totalReturn+result.getTotalReturn();

        }
        List<BacktestResult> buyList=Lists.newArrayList();
        List<BacktestResult> sellList=Lists.newArrayList();
        List<BacktestResult> holding=Lists.newArrayList();
        List<BacktestResult> notOpList=Lists.newArrayList();
        for (BacktestResult backtestResult : list) {
            if (backtestResult.isToDayBuy()){
                buyList.add(backtestResult);
            }else if(backtestResult.isToDaySell()){
                sellList.add(backtestResult);
            }else if(backtestResult.isHolding()){
                holding.add(backtestResult);
            }else{
                notOpList.add(backtestResult);
            }
        }
        List<BacktestResult> resultList=Lists.newArrayList();
        resultList.addAll(buyList);
        resultList.addAll(sellList);
        resultList.addAll(holding);
        resultList.addAll(notOpList);

        todayResultView.setResult(resultList);
        todayResultView.setTotalReturn(totalReturn);

        List<FinanceHttpApi.HttpHotSector> hotForEstMoney = FinanceHttpApi.getHotForEstMoney(true, 10);
        String hotIn=hotForEstMoney.stream()
                .map(FinanceHttpApi.HttpHotSector::getHotSector)
                .collect(Collectors.joining(","));

        todayResultView.setHotIn(hotIn);

        List<FinanceHttpApi.HttpHotSector> hotForEstMoneyOut = FinanceHttpApi.getHotForEstMoney(false, 10);
        String hotOut=hotForEstMoneyOut.stream()
                .map(FinanceHttpApi.HttpHotSector::getHotSector)
                .collect(Collectors.joining(","));
        todayResultView.setHotOut(hotOut);
        todayResultView.setPe300(FinanceHttpApi.get300Pe());
        todayResultView.setPe500(FinanceHttpApi.get500Pe());
        todayResultView.setPeCY(FinanceHttpApi.getCYPe());
        return todayResultView;

    }

    public static JSONArray getLastNRows(JSONArray originalArray, int n) {
        JSONArray result = new JSONArray();
        int totalLength = originalArray.size();
        int startIndex = Math.max(0, totalLength - n); // 确保索引不越界

        // 从 startIndex 开始遍历到最后
        for (int i = startIndex; i < totalLength; i++) {
            result.add(originalArray.get(i));
        }
        return result;
    }

    public static JSONArray getStockHistory(String code) {
        String stock = stock(code, false,180);
        JSONObject stockJSON = JSONObject.parseObject(stock);
        JSONArray jsonArray= stockJSON.getJSONArray("data");
        Double atr21 = stockJSON.getDouble("atr21");
        for (int i = 1; i < jsonArray.size(); i++) {
            Double close = jsonArray.getJSONObject(i).getDouble("close");
            Double yclose = jsonArray.getJSONObject(i-1).getDouble("close");
            Double chp=(close-yclose)/yclose*100;
            BigDecimal value = new BigDecimal(chp);
            value.setScale(2,RoundingMode.HALF_UP);
            double atr21pt = atr21 / close*100.00;
            jsonArray.getJSONObject(i).put("atr21pt", atr21pt);
            jsonArray.getJSONObject(i).put("chp", value.toString());
        }
        return jsonArray;
    }

    public static String stock(String code,boolean needHotInOut) {
        return stock(code,needHotInOut,180);
    }
    public static String stock(String code,boolean needHotInOut,Integer limit) {
        String api = "https://money.finance.sina.com.cn/quotes_service/api/jsonp_v2.php/a=/CN_MarketData.getKLineData?symbol=" + FinanceHttpApi.convertCode(code) + "&scale=" + 240 + "&ma=no&datalen="+limit;
        try {
            String result = NoHttpsClientUtils.getRestTemplate().getForObject(api, String.class);
            String filterResult = StringUtils.substringAfter(result, "*/");
            filterResult = StringUtils.replace(filterResult, "a=(", "");
            filterResult = filterResult.substring(0, filterResult.length() - 2);
            JSONArray jsonArray = getLastNRows(JSON.parseArray(filterResult),limit);
            Map<String,Object> map=Maps.newHashMap();
            map.put("data",jsonArray);
            map.put("code","200");

            List<FinanceHttpApi.HttpStockData> stockData = FinanceHttpApi.getNow(code);
            FinanceHttpApi.HttpStockData today = stockData.get(0);

            boolean hadToday=false;
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject history = jsonArray.getJSONObject(i);
                String historyDate = StringUtils.replace(history.getString("day"), "-", "");
                history.put("day",historyDate);
                if(historyDate.equals(today.getCurDate())){
                    hadToday=true;
                }
            }
            if(!hadToday){
                JSONObject todayObject = new JSONObject();
                todayObject.put("day",today.getCurDate());
                todayObject.put("volume",today.getVolumeMoney().doubleValue()*100);
                todayObject.put("high",today.getHigh());
                todayObject.put("low",today.getLow());
                todayObject.put("close",today.getClose());
                todayObject.put("open",today.getOpen());
                jsonArray.add(todayObject);
            }

            BigDecimal prePrice = jsonArray.getJSONObject(jsonArray.size() - 2).getBigDecimal("close");


            BigDecimal ma20 = getMaValue(jsonArray, 20);

            BigDecimal ma5 = getMaValue(jsonArray, 5);

            BigDecimal ma10 = getMaValue(jsonArray, 10);

            BigDecimal ma14 = getMaValue(jsonArray, 14);

            BigDecimal ma60 = getMaValue(jsonArray, 60);

            today.setMa5(ma5);
            today.setMa10(ma10);
            today.setMa20(ma20);
            today.setMa14(ma14);
            today.setMa60(ma60);
            today.setStockCode(code);
            today.setPrePrice(prePrice);
            

            JSONObject yStock = jsonArray.getJSONObject(jsonArray.size() - 2);
            BigDecimal yStockClose = yStock.getBigDecimal("close");
            if(yStockClose.doubleValue()<=ma20.doubleValue() && today.getClose().doubleValue()>ma20.doubleValue()){
                today.setOverMa20(true);
            }else{
                today.setOverMa20(false);
            }

            map.put("stock", today);

            double[] rsi1 = new double[jsonArray.size()];
            double[] rsi2 = new double[jsonArray.size()];
            double[] rsi3 = new double[jsonArray.size()];
            double[] closeArry = new double[jsonArray.size()];
            List<BigDecimal> allClose=Lists.newArrayList();
            for (int i = 0; i < jsonArray.size(); i++) {
                BigDecimal close = jsonArray.getJSONObject(i).getBigDecimal("close");
                allClose.add(close);
                closeArry[i]=close.doubleValue();
            }

            IndicatrixUtils.rsi(closeArry,5,10,20,rsi1,rsi2,rsi3);

            for (int i = 0; i < jsonArray.size(); i++) {
                jsonArray.getJSONObject(i).put("rsi",rsi1[i]);
            }

            today.setRsi(jsonArray.getJSONObject(jsonArray.size()-1).getBigDecimal("rsi"));
            for (int i = jsonArray.size() - 1; i >= 0; i--) {
                if(jsonArray.getJSONObject(i).getDouble("rsi").doubleValue()<=0){
                    jsonArray.remove(i);
                }
            }

            List<Double> trList = new ArrayList<>();
            for (int i = 1; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                double H = jsonObject.getBigDecimal("high").doubleValue();
                double L = jsonObject.getBigDecimal("low").doubleValue();
                double PC = jsonArray.getJSONObject(i-1).getBigDecimal("close").doubleValue();
                double close = jsonArray.getJSONObject(i).getBigDecimal("close").doubleValue();
                double tr = Math.max(Math.max(H - L, Math.abs(H - PC)),Math.abs(PC - L));
                tr=Math.abs(close-PC);
                trList.add(tr);
            }
            double atr20 = 0;
            for (int i = trList.size() - 61; i < trList.size(); i++) {
                atr20 += trList.get(i);
            }
            atr20 /= 61;
            //波动率使用80%
            double atr20Percent = atr20 / today.getClose().doubleValue() ;
            map.put("atr21", atr20);
            map.put("atr60", atr20);
            map.put("atr21Percent", BaseUtils.doubleToPtStr(atr20Percent));

            if(needHotInOut) {
                List<FinanceHttpApi.HttpHotSector> hotForEstMoney = FinanceHttpApi.getHotForEstMoney(true, 10);
                String hotIn = hotForEstMoney.stream()
                        .map(FinanceHttpApi.HttpHotSector::getHotSector)
                        .collect(Collectors.joining(","));

                map.put("hotIn", hotIn);
                List<FinanceHttpApi.HttpHotSector> hotForEstMoneyOut = FinanceHttpApi.getHotForEstMoney(false, 10);
                String hotOut = hotForEstMoneyOut.stream()
                        .map(FinanceHttpApi.HttpHotSector::getHotSector)
                        .collect(Collectors.joining(","));
                map.put("hotOut", hotOut);
            }
            return JSONObject.toJSONString(map);
        } catch (Exception e) {
            return "ERROR";
        }
    }

    private static BigDecimal getMaValue(JSONArray jsonArray, int x) {
        List<BigDecimal> ma20List = Lists.newArrayList();
        for (int i = jsonArray.size() - x; i < jsonArray.size(); i++) {
            ma20List.add(new BigDecimal(jsonArray.getJSONObject(i).getString("close")));
        }
        BigDecimal ma20 = IndicatrixUtils.avg(ma20List);
        return ma20;
    }

    public void refreshPower(){
        try {
            String code = "601398,000063,000301,000338,000538,000568,000596,000625,000768,000786,000792,000800,000807,000858,000938,000963,000975,000977,000999,001289,001965,002001,002007,002027,002028,002049,002050,002074,002129,002179,002180,002230,002241,002252,002311,002352,002371,002415,002422,002460,002463,002475,002594,002648,002709,002714,002916,002920,002938,003816,300014,300015,300033,300059,300124,300274,300308,300316,300347,300394,300408,300413,300418,300433,300442,300450,300498,300502,300628,300661,300750,300759,300760,300782,300832,300896,300979,300999,301269,600009,600010,600023,600025,600026,600029,600031,600061,600066,600085,600111,600115,600150,600160,600161,600183,600196,600233,600276,600362,600372,600406,600415,600436,600460,600482,600489,600515,600519,600547,600570,600584,600588,600600,600660,600674,600690,600745,600760,600795,600809,600845,600875,600886,600887,600893,600900,600905,600918,600938,600941,600989,601012,601021,601059,601066,601100,601111,601117,601127,601136,601138,601236,601318,601319,601336,601360,601601,601607,601618,601628,601633,601688,601689,601698,601728,601766,601799,601808,601816,601865,601868,601877,601878,601899,601901,601985,601989,601995,603019,603195,603259,603260,603288,603296,603369,603392,603501,603659,603799,603806,603986,603993,605117,605499,688008,688012,688041,688082,688111,688126,688169,688187,688223,688256,688271,688303,688396,688472,688506,688981";
            code = code + ",000009,000021,000032,000034,000050,000060,000066,000400,000403,000423,000426,000519,000537,000559,000563,000623,000629,000636,000723,000729,000738,000739,000818,000831,000878,000887,000893,000898,000987,000988,000997,000998,001286,002019,002025,002044,002065,002085,002128,002138,002152,002153,002155,002156,002195,002202,002203,002240,002244,002261,002262,002266,002268,002273,002281,002294,002299,002318,002340,002353,002368,002385,002407,002409,002410,002414,002429,002436,002439,002444,002465,002472,002517,002557,002558,002568,002595,002603,002607,002608,002625,002653,002670,002683,002739,002797,002850,002945,002984,003022,003031,003035,300001,300002,300012,300017,300024,300037,300054,300058,300114,300118,300136,300140,300142,300144,300207,300212,300223,300251,300285,300339,300373,300395,300474,300476,300487,300496,300529,300558,300567,300573,300595,300604,300676,300699,300724,300748,300751,300763,300765,300803,300866,300919,300957,301165,301236,301267,301301,301308,301358,301498,600008,600021,600032,600038,600062,600079,600095,600096,600098,600118,600129,600131,600143,600166,600208,600258,600271,600298,600299,600312,600316,600329,600350,600361,600378,600392,600416,600418,600435,600487,600498,600500,600516,600517,600521,600529,600536,600563,600580,600583,600598,600606,600637,600642,600685,600688,600754,600763,600764,600765,600816,600839,600848,600862,600871,600879,600885,600895,600906,600956,600968,600977,600988,600995,601061,601099,601106,601118,601128,601156,601162,601168,601179,601198,601212,601399,601456,601555,601567,601608,601696,601717,601858,601933,601958,601965,601966,601990,601992,603000,603056,603087,603129,603160,603179,603233,603290,603298,603338,603341,603345,603379,603486,603556,603568,603596,603605,603606,603650,603658,603707,603712,603728,603737,603786,603826,603858,603893,603927,603939,603979,605358,688002,688005,688017,688032,688047,688052,688065,688072,688099,688114,688120,688122,688153,688172,688180,688188,688213,688220,688234,688248,688249,688276,688278,688281,688295,688297,688301,688331,688349,688361,688363,688375,688385,688387,688390,688469,688475,688516,688520,688521,688525,688536,688538,688561,688563,688567,688578,688617,688777,689009";

            String A300 = ",600941,600519,601398,601939,601857,601288,601988,601628,601318,600036,300750,002594,600028,601088,000858,600900,601728,601658,601328,688981,000333,601138,601166,600030,300760,601899,002415,000568,601601,601998,600809,600276,600309,601633,603259,601319,601816,300059,002475,601668,600690,000001,600000,603288,601012,601888,002352,000651,002714,601066";
            A300 += ",601818,601225,600406,002304,601995,300999,600104,300124,001289,600887,600016,002142,600050,688111,300015,601919,601766,003816,601390,600188,600436,000725,000625,601688,000063,600048,601985,000002,600019,000596,600905,300122,600025,600438,601800,600585,600919,002371,601211,600837,600031,002230,300274,601881,600999,603993,601111,603501,601898,300498";
            A300 += ",600011,600150,600018,000338,601336,688036,600547,002493,600600,601006,000776,601186,600760,000166,001979,601238,600845,600029,600989,601600,002050,600660,002027,600346,688012,601169,600893,601989,601868,000538,600886,600115,600015,002736,601669,688223,603195,000792,300014,000895,002460,002466,600009,601229,601698,002179,002129,601009,300033,300896";
            A300 += ",601100,000617,600111,688303,002459,601808,000100,600196,601788,000708,605499,600010,002311,600089,601689,600958,000963,002236,000425,601618,000301,603369,688396,600039,002920,600085,300433,300782,600426,603986,600674,601360,603260,600795,601607,000877,600926,601865,600362,688599,300408,300979,002049,002271,601901,000768,603799,600741,002241,600570";
            A300 += ",300347,000938,002812,300316,002821,688008,603392,601699,600745,000157,600588,603833,601377,000983,000661,300759,002648,603019,601872,603659,688187,600803,600584,600176,601838,688126,002709,002001,300413,601877,600918,601021,600332,603806,000786,002938,600875,600233,000876,002601,002252,002555,000977,601117,601799,300628,600061,000800,002074,300450";
            A300 += ",002007,600183,601878,688363,002202,600132,300957,300661,600732,300142,300454,601236,600219,002841,300919,002180,600606,002064,300751,000408,600754,601615,002410,002916,000733,600460,601216,688561,300223,002414,300496,300601,603899,300763,000069,688065,605117,600884,300207,000723,603290,002120,600763,603486,601155,002756,600383,603185,300769,688005";

            code = code + A300;
//       String code="688563";

            List<String> resultList=Lists.newArrayList();
            for (String s : StringUtils.split(code, ",")) {
                try {
                    Boolean isOverMa120 = isOverMa(s, 240, 200);
                    Thread.sleep(500 + (int) (Math.random() * 501));
                    if (isOverMa120) {
                        System.out.println(s);
                        resultList.add(s);
                    }
                } catch (Exception e) {
                    System.out.println("code error=" + s);
                    e.printStackTrace();
                }
            }
            GuavaCacheUtils.nbList=resultList;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean isOverMa(String code, int sec, int maDay) throws Exception {
        /*
        1、当前股价处在150日和200日均线上方
        2、150日均线处在200日均线上方
        3、200日均线至少上涨了1个月（大多数情况下，上涨4〜5个月更好）
        4、50日移动平均值高于150日及200日移动平均值
        5、当前价格高于50日移动平均值
        6、当前股价比最近一年最低股价至少高30%
        7、当前价格至少处在最近一年最高价的75％以内（距离最高价越近越好）
        */
        String api = "https://money.finance.sina.com.cn/quotes_service/api/jsonp_v2.php/a=/CN_MarketData.getKLineData?symbol=" + FinanceHttpApi.convertCode(code) + "&scale=" + sec + "&maDay=no&datalen=" + 367;
        String result = NoHttpsClientUtils.getRestTemplate().getForObject(api, String.class);
        String filterResult = StringUtils.substringAfter(result, "*/");
        filterResult = StringUtils.replace(filterResult, "a=(", "");
        filterResult = filterResult.substring(0, filterResult.length() - 2);
        JSONArray jsonArray = JSON.parseArray(filterResult);

        List<BigDecimal> closeList = Lists.newArrayList();
        double[] closeArry = new double[jsonArray.size()];
        double[] hgArry = new double[jsonArray.size()];
        double[] lowArry = new double[jsonArray.size()];
        double[] volume30Arry = new double[jsonArray.size()];
        double[] volumeArry = new double[jsonArray.size()];
        for (int j = 0; j < jsonArray.size(); j++) {
            JSONObject jsonObject = jsonArray.getJSONObject(j);
            Double close = jsonObject.getDouble("close");
            closeArry[j] = close;
            Double hg = jsonObject.getDouble("high");
            hgArry[j] = hg;
            Double low = jsonObject.getDouble("low");
            lowArry[j] = low;
            closeList.add(new BigDecimal(close));
            Long ma_volume30 = jsonObject.getLong("ma_volume30");
            volume30Arry[j]=ma_volume30==null?999999999999L:ma_volume30;
            Long volume = jsonObject.getLong("volume");
            volumeArry[j]=volume;

        }
        double[] maL = IndicatrixUtils.ma(maDay, closeArry);
        double[] maM = IndicatrixUtils.ma(maDay - (maDay / 4), closeArry);
        //50MA
        double[] maMM = IndicatrixUtils.ma(maDay / 4, closeArry);

        double[] ma20Arry = IndicatrixUtils.ma(maDay / 10, closeArry);

        double[] lowMa = IndicatrixUtils.ma(maDay / 4, lowArry);
        double[] hgMa = IndicatrixUtils.ma(maDay / 4, hgArry);


        BigDecimal min = IndicatrixUtils.min(closeList);
        BigDecimal max = IndicatrixUtils.max(closeList);
        int toDayIndex = jsonArray.size() - 1;
        double ma200 = maL[toDayIndex];
        double ma150 = maM[toDayIndex];
        double ma50 = maMM[toDayIndex];
        double ma20 = ma20Arry[toDayIndex];
        double now = closeArry[toDayIndex];

        // 当前股价处在150日和200日均线上方
        // 150日均线处在200日均线上方
        //50日移动平均值高于150日及200日移动平均值
        boolean baseCondition=now>ma200 && now >ma150 && ma150>ma200 && ma50>ma150 ;
        boolean volumeCondition=volumeArry[toDayIndex]>=volume30Arry[toDayIndex]*1.2;

        //当前股价比最近一年最低股价至少高30%
        boolean isOver30 = (now - min.doubleValue()) / min.doubleValue() > 0.3;
        //最近一年最低价与最高价至少30%
        boolean down30 = (min.doubleValue() - max.doubleValue()) / max.doubleValue() <-0.3;
        //50天线至少比最高位回落10%
        boolean ma20Down = (ma20 - max.doubleValue()) / max.doubleValue() <-0.1  && ma20>ma50;
        boolean ma20DiffMa50 =ma20>ma50*1.1;
        //当前价格至少处在最近一年最高价的75％以内（距离最高价越近越好）
        boolean isOverMax=now>max.doubleValue()*0.85;
        //5、当前价格高于50日移动平均值
        boolean isOverNday=now>ma50;
        if (ma20DiffMa50 && ma20Down && down30 &&  isOverMax && isOver30 && isOverNday && baseCondition
//                && volumeCondition
        ) {

            boolean isUp = (now - ma200) / now > 0.03;
            if (isUp) {
                return true;
            }
            for (int i = 1; i < 4; i++) {
                if ((closeArry[closeArry.length - i] < ma200)) {
                    return false;
                }
                if ((maMM[maMM.length - i] < maM[maM.length - i - 1])) {
                    return false;
                }
                if ((lowMa[lowMa.length - i] < lowMa[lowMa.length - i - 1])) {
                    return false;
                }
                if ((hgMa[lowMa.length - i] < hgMa[lowMa.length - i - 1])) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    private BigDecimal vcp(double[] closeArry,int beforeDay) {
        List<BigDecimal> downPList=Lists.newArrayList();
        List<BigDecimal> closeList=Lists.newArrayList();
        for (int i =closeArry.length-beforeDay; i < closeArry.length; i++) {
            closeList.add(new BigDecimal(closeArry[i]));
        }
        BigDecimal max = IndicatrixUtils.max(closeList);
        for (int j = closeArry.length-beforeDay; j < closeArry.length; j++) {
            double downP = (closeArry[j] - max.doubleValue()) / max.doubleValue();
            if(downP<0){
                downPList.add(new BigDecimal(downP));
            }
        }
        if(downPList.size()==0){
            return new BigDecimal(0.00);
        }
        BigDecimal maxDownP = IndicatrixUtils.min(downPList);
        return maxDownP;
    }

    public StockReportMinuteRsiView analysisMinuteRsi(String code, int sec) throws Exception {
        Double buy = null;
        Double maxPrice = null;
        List<DailyStockData> byCodeList = getByCode(code);
        DailyStockData cur = byCodeList.get(byCodeList.size() - 1);
        StockReportMinuteRsiView rsiView = new StockReportMinuteRsiView();
        rsiView.setToDay(cur);
        rsiView.setDayList(Lists.newArrayList());
        rsiView.setRsiList(Lists.newArrayList());
        rsiView.setCloseList(Lists.newArrayList());
        rsiView.setMa5List(Lists.newArrayList());
        rsiView.setMa20List(Lists.newArrayList());
        rsiView.setNear10MinList(Lists.newArrayList());
        rsiView.setClose20MaxList(Lists.newArrayList());
        rsiView.setBuyPointViewList(Lists.newArrayList());
        rsiView.setMacdList(Lists.newArrayList());
        rsiView.setMacdDeaList(Lists.newArrayList());
        rsiView.setMacdDiffList(Lists.newArrayList());
        rsiView.setHighMoreList(Lists.newArrayList());
        rsiView.setLowMoreList(Lists.newArrayList());
        rsiView.setOpenList(Lists.newArrayList());
        rsiView.setChpMean(0.0);
        rsiView.setDesc("");
        rsiView.setVolumeTipsList(Lists.newArrayList());
        String api = "https://money.finance.sina.com.cn/quotes_service/api/jsonp_v2.php/a=/CN_MarketData.getKLineData?symbol=" + FinanceHttpApi.convertCode(code) + "&scale=" + sec + "&ma=no&datalen=" + 365 * 3;
        String result = NoHttpsClientUtils.getRestTemplate().getForObject(api, String.class);
        String filterResult = StringUtils.substringAfter(result, "*/");
        filterResult = StringUtils.replace(filterResult, "a=(", "");
        filterResult = filterResult.substring(0, filterResult.length() - 2);
        JSONArray jsonArray = JSON.parseArray(filterResult);

        if(sec==240) {
            List<FinanceHttpApi.HttpStockData> stockData = FinanceHttpApi.getNow(code);
            FinanceHttpApi.HttpStockData today = stockData.get(0);

            boolean hadToday = false;
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject history = jsonArray.getJSONObject(i);
                String historyDate = StringUtils.replace(history.getString("day"), "-", "");
                history.put("day", historyDate);
                if (historyDate.equals(today.getCurDate())) {
                    hadToday = true;
                }
            }
            if (!hadToday) {
                JSONObject todayObject = new JSONObject();
                todayObject.put("day", today.getCurDate());
                todayObject.put("volume", today.getVolume().toString());
                todayObject.put("high", today.getHigh());
                todayObject.put("low", today.getLow());
                todayObject.put("close", today.getClose());
                todayObject.put("open", today.getOpen());
                jsonArray.add(todayObject);
            }
        }

        double[] closeArry = new double[jsonArray.size()];
        double[] close20MaxArry = new double[jsonArray.size()];
        double[] rsi1 = new double[jsonArray.size()];
        double[] rsi2 = new double[jsonArray.size()];
        double[] rsi3 = new double[jsonArray.size()];
        double[] vol = new double[jsonArray.size()];
        double[] macd = new double[jsonArray.size()];
        double[] macdDiff = new double[jsonArray.size()];
        double[] macdDea = new double[jsonArray.size()];



        List<Double> volList = Lists.newArrayList();
        for (int j = 0; j < jsonArray.size(); j++) {

            JSONObject jsonObject = jsonArray.getJSONObject(j);
            Double close = jsonObject.getDouble("close");
            closeArry[j] = close;
            Double volume = jsonObject.getDouble("volume");
            vol[j] = volume;
            volList.add(volume);
            rsiView.getDayList().add(jsonObject.getString("day"));
            rsiView.getCloseList().add(jsonObject.getBigDecimal("close"));
            rsiView.getOpenList().add(jsonObject.getBigDecimal("open"));
            rsiView.getNear10MinList().add(jsonObject.getBigDecimal("close"));
            rsiView.getClose20MaxList().add(jsonObject.getBigDecimal("close"));
            rsiView.getMacdList().add(new BigDecimal("0.00"));
            rsiView.getMacdDiffList().add(new BigDecimal("0.00"));
            rsiView.getMacdDeaList().add(new BigDecimal("0.00"));
            rsiView.getHighMoreList().add(new Double("0.00"));
            rsiView.getLowMoreList().add(new Double("0.00"));


        }
        IndicatrixUtils.macd(closeArry, 7, 14, 21, macd, macdDea, macdDiff);
        IndicatrixUtils.rsi(closeArry, 7, 14, 21, rsi1, rsi2, rsi3);
        for (double v : rsi1) {
            rsiView.getRsiList().add(BaseUtils.stringToBigDecimal(BaseUtils.format(v)));
        }
        double topVol = IndicatrixUtils.getPercentile2(volList, 0.7);

        double[] ma = IndicatrixUtils.ma(20, closeArry);
        for (double m : ma) {
            rsiView.getMa5List().add(new BigDecimal(m));
        }
        double[] ma20 = IndicatrixUtils.ma(20, closeArry);
        for (double m : ma20) {
            rsiView.getMa20List().add(new BigDecimal(m));
        }

        List<Double> rsiList = Lists.newArrayList();
        for (double v : rsi1) {
            rsiList.add(v);
        }
        double lowRsi = IndicatrixUtils.getPercentile2(rsiList, 0.1);
        double topRsi = IndicatrixUtils.getPercentile2(rsiList, 0.9);
        rsiView.setLow20Rsi(new BigDecimal(lowRsi));
        rsiView.setTop80Rsi(new BigDecimal(topRsi));
        Map<String, JSONObject> buyMap = Maps.newHashMap();
        Map<String, JSONObject> sellMap = Maps.newHashMap();


        List<BigDecimal> upList = Lists.newArrayList();
        for (int j = 0; j < jsonArray.size(); j++) {
            rsiView.getMacdList().set(j, new BigDecimal(macd[j] * 100));
            rsiView.getMacdDeaList().set(j, new BigDecimal(macdDea[j] * 100));
            rsiView.getMacdDiffList().set(j, new BigDecimal(macdDiff[j] * 100));
        }
        double lowMacdDiff = IndicatrixUtils.getPercentile2(IndicatrixUtils.toDouble(rsiView.getMacdDiffList()), 0.1);
        double topMacdDiff = IndicatrixUtils.getPercentile2(IndicatrixUtils.toDouble(rsiView.getMacdDiffList()), 0.9);
        double[] macdDiff100 = rsiView.getMacdDiffList().stream()
                .mapToDouble(BigDecimal::doubleValue)
                .toArray();

        List<Double> trList = new ArrayList<>();
        for (int i = 1; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            double H = jsonObject.getBigDecimal("high").doubleValue();
            double L = jsonObject.getBigDecimal("low").doubleValue();
            double PC = jsonArray.getJSONObject(i-1).getBigDecimal("close").doubleValue();
            double tr = Math.max(Math.max(H - L, Math.abs(H - PC)),Math.abs(PC - L));
            trList.add(tr);
        }

        JSONObject rangeChpJsonObject = jsonArray.getJSONObject(110);
        BigDecimal first = rangeChpJsonObject.getBigDecimal("close");
        rangeChpJsonObject = jsonArray.getJSONObject(jsonArray.size() - 1);
        BigDecimal last = rangeChpJsonObject.getBigDecimal("close");
        rsiView.setRangeChpsum((last.doubleValue() - first.doubleValue()) / first.doubleValue() * 100);


        for (int j = 130; j < jsonArray.size(); j++) {

            JSONObject jsonObject = jsonArray.getJSONObject(j);
            BigDecimal close = jsonObject.getBigDecimal("close");


            {
                BigDecimal near10Close = new BigDecimal(close.doubleValue());
                List<BigDecimal> near10CloseList = Lists.newArrayList();
                for (int i = j - 17; i < j - 7; i++) {
                    JSONObject near10CloseJSON = jsonArray.getJSONObject(i);
                    near10CloseList.add(near10CloseJSON.getBigDecimal("close"));
                }
                near10Close = IndicatrixUtils.min(near10CloseList);

                BigDecimal near20Max = IndicatrixUtils.max(near10CloseList);

                rsiView.getNear10MinList().set(j, IndicatrixUtils.avg(Lists.newArrayList(near10Close, rsiView.getMa20List().get(j))));
//                rsiView.getNear10MinList().set(j,IndicatrixUtils.avg(near10CloseList));
                rsiView.getClose20MaxList().set(j, IndicatrixUtils.avg(Lists.newArrayList(near20Max, rsiView.getNear10MinList().get(j))));
            }

            BigDecimal min = new BigDecimal(close.doubleValue());
            List<BigDecimal> minList = Lists.newArrayList();
            List<BigDecimal> diffList = Lists.newArrayList();


            for (int i = j - 60; i < j; i++) {
                minList.add(jsonArray.getJSONObject(i).getBigDecimal("close"));
                BigDecimal diffPrice = new BigDecimal(jsonArray.getJSONObject(i).getBigDecimal("close").doubleValue() - jsonArray.getJSONObject(i - 1).getBigDecimal("close").doubleValue());
                diffList.add(new BigDecimal(Math.abs(diffPrice.doubleValue())));
                min = IndicatrixUtils.min(minList);
            }


            List<BigDecimal> listForD3N30 = Lists.newArrayList();
            List<BigDecimal> listForD3N60 = Lists.newArrayList();


            for (int i = j - 5 * 4 - 5; i < j - 5; i++) {
                listForD3N30.add(jsonArray.getJSONObject(i).getBigDecimal("close"));
            }


            for (int i = j - 5 * 4*6 - 5; i < j - 5; i++) {
                listForD3N60.add(jsonArray.getJSONObject(i).getBigDecimal("close"));
            }


            rsiView.setTopClose(CollectionUtils.isNotEmpty(listForD3N30) ? IndicatrixUtils.max(listForD3N30).doubleValue() : 999);
            rsiView.setLowClose(CollectionUtils.isNotEmpty(listForD3N30) ? IndicatrixUtils.min(listForD3N30).doubleValue() : 0);
            rsiView.setTopClose60(CollectionUtils.isNotEmpty(listForD3N60) ? IndicatrixUtils.max(listForD3N60).doubleValue() : 999);
            rsiView.setLowClose60(CollectionUtils.isNotEmpty(listForD3N60) ? IndicatrixUtils.min(listForD3N60).doubleValue() : 0);
            if (j == jsonArray.size() - 1) {


                double atr20 = 0;
                for (int i = trList.size() - 21; i < trList.size(); i++) {
                    atr20 += trList.get(i);
                }
                atr20 /= 21;
                double atr20Percent = atr20 / close.doubleValue() ;
                rsiView.setChpMean(atr20Percent*0.8 * 100);
            }
            //rsiView.getMa20List().set(j, min);

            BigDecimal minAvg5 = new BigDecimal(close.doubleValue());
            List<BigDecimal> minAvg5List = Lists.newArrayList();
            if (j >= 16) {
                for (int i = j - 16; i < j; i++) {
                    minAvg5List.add(rsiView.getMa20List().get(i));
                    minAvg5 = IndicatrixUtils.avg(minList);
                }
            }

            BigDecimal minMacdDiff = new BigDecimal(close.doubleValue());
            List<BigDecimal> minMacdDiff30List = Lists.newArrayList();

            List<BigDecimal> volume30List = Lists.newArrayList();
            List<BigDecimal> chp30List = Lists.newArrayList();

            List<BigDecimal> close30List = Lists.newArrayList();
            List<Double> open30List = Lists.newArrayList();
            if (j >= 60) {
                for (int i = j - 30; i < j; i++) {
                    minMacdDiff30List.add(rsiView.getMacdDiffList().get(i));
                    minMacdDiff = IndicatrixUtils.min(minMacdDiff30List);
                    BigDecimal volume = jsonArray.getJSONObject(i).getBigDecimal("volume");
                    volume30List.add(volume);
                    double chp = getChp(jsonArray, i);
                    chp30List.add(new BigDecimal(Math.abs(chp)));
                }

                if (jsonArray.getJSONObject(j - 1).getBigDecimal("volume").doubleValue() < IndicatrixUtils.avg(volume30List).doubleValue() * 0.2
                        && jsonArray.getJSONObject(j).getBigDecimal("volume").doubleValue() > jsonArray.getJSONObject(j - 1).getBigDecimal("volume").doubleValue() * 1.2
                        && jsonArray.getJSONObject(j - 1).getBigDecimal("volume").doubleValue() <= IndicatrixUtils.min(volume30List).doubleValue()
                ) {
                    String topVolumeDay = rsiView.getDayList().get(j);
                    ChartPointView topVolumeTips = new ChartPointView();
                    ListStyleView listStyle = new ListStyleView();
                    listStyle.setType("solid");
                    topVolumeTips.setItemStyle(listStyle);
                    topVolumeTips.setXAxis(topVolumeDay);
                    topVolumeTips.setSymbol("none");
                    listStyle.setColor("gold");
                    rsiView.getVolumeTipsList().add(topVolumeTips);
                }

                if (jsonArray.getJSONObject(j).getBigDecimal("volume").doubleValue() > IndicatrixUtils.avg(volume30List).doubleValue() * 4
                        && jsonArray.getJSONObject(j).getBigDecimal("volume").doubleValue() > IndicatrixUtils.max(volume30List).doubleValue()
                        && Math.abs(getChp(jsonArray, j)) > IndicatrixUtils.avg(chp30List).doubleValue() * 2
                ) {
                    String topVolumeDay = rsiView.getDayList().get(j);
                    ChartPointView topVolumeTips = new ChartPointView();
                    ListStyleView listStyle = new ListStyleView();
                    listStyle.setType("solid");
                    topVolumeTips.setItemStyle(listStyle);
                    topVolumeTips.setXAxis(topVolumeDay);
                    topVolumeTips.setSymbol("none");
                    if (closeArry[j] > closeArry[j - 1]) {
                        listStyle.setColor("red");
                    } else {
                        listStyle.setColor("green");
                    }

                    rsiView.getVolumeTipsList().add(topVolumeTips);
                }
            }

            if (j >= 4 * 20) {
                for (int i = j - 4 * 20; i < j; i++) {
                    close30List.add(new BigDecimal(rsiView.getCloseList().get(i).doubleValue()));
                    open30List.add(rsiView.getOpenList().get(i).doubleValue());
                }
            }
            double[][] openAndClose = new double[4 * 20][2];
            for (int i = 0; i < close30List.size(); i++) {
                openAndClose[i][0] = close30List.get(i).doubleValue();
                openAndClose[i][1] = open30List.get(i);
            }

            double[] mostOverlapPodoubles = IndicatrixUtils.findMostOverlapPodoubles(openAndClose);
            double hmore = IndicatrixUtils.avg(Lists.newArrayList(new BigDecimal(mostOverlapPodoubles[0]), new BigDecimal(mostOverlapPodoubles[1]))).doubleValue();
            rsiView.getHighMoreList().set(j, IndicatrixUtils.avg(close30List).doubleValue());


            if (close.doubleValue() - min.doubleValue() > 0) {
                upList.add(new BigDecimal(close.doubleValue() - min.doubleValue()));
            }
            BigDecimal upAvg = IndicatrixUtils.avg(upList);


//                System.out.println(min +","+rsi1[j-1]+ "【CHECK】" + rsi1[j] + jsonObject.toJSONString());

            JSONObject yestoday = jsonArray.getJSONObject(j - 1);
            JSONObject yestoday2 = jsonArray.getJSONObject(j - 3);

            double near10Min = rsiView.getNear10MinList().get(j).doubleValue();
            double ma20Value = rsiView.getMa20List().get(j).doubleValue();
            double highMore = rsiView.getHighMoreList().get(j).doubleValue();
//            rsiView.getHighMoreList().set(j,rsiView.getTopClose60());

            if (buy != null && closeArry[j] > buy) {
                maxPrice = closeArry[j];
            }

            Map<String, Boolean> buyConditionMap = Maps.newLinkedHashMap();
            buyConditionMap.put(jsonObject.getString("day"), true);
            buyConditionMap.put(">j-1", close.doubleValue() > closeArry[j - 1]);
//            buyConditionMap.put("isLowRange(closeArry, j, near10Min,8)", isLowRange(closeArry, j, near10Min,4*7));
//            buyConditionMap.put("isLowRange(rsi1, j, lowRsi,15)", isLowRange(rsi1, j, lowRsi,4*20) && rsi1[j]>50);
            buyConditionMap.put("sLowRange(macdDiff100, j, minMacdDiff.doubleValue(),32)", isLowRange(macdDiff100, j, minMacdDiff.doubleValue(), 5 * 20));
//            buyConditionMap.put("closeArry[j]>=ma20",closeArry[j]>=ma20 );
//            buyConditionMap.put("closeArry[j-1]<=ma20",closeArry[j-1]<=ma20 );
//            buyConditionMap.put("rsi<lowRsi && rsi <30",rsi1[j]<lowRsi && rsi1[j]<30 );
//            buyConditionMap.put("loseArry[j]> highMore",closeArry[j-1]<= highMore);

            buyConditionMap.put("rsi1[j-2] <= 201 ", rsi1[j - 2] <= 20);
            buyConditionMap.put("rsi1[j-2] <= 20 ", rsi1[j - 1] >= rsi1[j - 2]);
//            buyConditionMap.put("rsi<lowRsi && rsi <30",  (rsi2[j-1]-rsi1[j-1])<10);

            buyConditionMap.put("closeArry[j - 1]<closeArry[j]", closeArry[j - 1] < closeArry[j]);


            buyConditionMap.put("loseArry[j]> highMore", closeArry[j - 2] <= rsiView.getLowClose60());
//           buyConditionMap.put("loseArry[j]> highMore", closeArry[j - 2]*0.98 <= rsiView.getLowClose60() &&  closeArry[j - 1]>= rsiView.getLowClose60() && closeArry[j] > rsiView.getLowClose60());

//            ?if (j >= (jsonArray.size() - 300)) {
//                System.out.println("=========================detail===============================");
//                for (String s : buyConditionMap.keySet()) {
//                    if(closeArry[j - 2] <= rsiView.getLowClose60() && closeArry[j] > rsiView.getLowClose60()){
//                        System.out.println(s + "=" + buyConditionMap.get(s) + ",rsiView.getLowClose():" + rsiView.getLowClose60());
//                        System.out.println(s + "=" + buyConditionMap.get(s) + ",csloseArry[j-3]:" + closeArry[j - 5]);
//                        System.out.println(s + "=" + buyConditionMap.get(s) + ",csloseArry[j]:" + closeArry[j]);
//                        System.out.println(s + "=" + buyConditionMap.get(s) + ",rsi1[j-1] :" + rsi1[j - 1]);
//                        System.out.println(s + "=" + buyConditionMap.get(s) + ",rsi1[j] :" + rsi1[j]);
//                    }
//                }
//            }

//            buyConditionMap.put("loseArry[j-2]<near10Min",closeArry[j-2]<near10Min);

            Boolean buyConditionValue = buyConditionMap.values().stream().allMatch(p -> p);

            if (buyConditionValue
            ) {
                buy = closeArry[j];

                System.out.println("【B】" + "rsi1[j-2]=" + rsi1[j - 2] + "," + rsi2[j - 1] + "rsi2[j-2]-rsi2[j-2]=" + (rsi2[j - 2] - rsi1[j - 2]) + ",rsi1[j]=" + rsi1[j] + jsonObject.toJSONString());
//                System.out.println("【B】"+"rsi1[j-2] ="+rsi1[j-1] +",json="+ jsonObject.toJSONString());

                ChartPointView chartPointView = new ChartPointView();
                chartPointView.setName("【B】" + jsonObject.getString("day"));
                chartPointView.setXAxis(jsonObject.getString("day"));
                chartPointView.setYAxis(BaseUtils.stringToBigDecimal(BaseUtils.format(close.doubleValue())));
                int buyRange = 2;
                if (sec == 15) {
                    buyRange = 2 * 4;
                }
                if (j >= (jsonArray.size() - buyRange)) {
                    chartPointView.setValue("BUYNOW");
                } else {
                    chartPointView.setValue("B");
                }
                rsiView.getBuyPointViewList().add(chartPointView);
                rsiView.setDesc(rsiView.getDesc() + "【B】" + jsonObject.getString("day") + "<br/>");
            }

            boolean overUp = (close.doubleValue() - min.doubleValue()) > upAvg.doubleValue();
            double maxClose = rsiView.getClose20MaxList().get(j).doubleValue();
            boolean downMACD = rsiView.getMacdDiffList().get(j - 1).doubleValue() >= topMacdDiff
                    && rsiView.getMacdDiffList().get(j).doubleValue() <= topMacdDiff;
            boolean downRsi = rsi1[j - 1] >= 85 && rsi1[j] < 85;
            Map<String, Boolean> sellConditionMap = Maps.newHashMap();
            sellConditionMap.put("1", rsi1[j - 1] <= rsi1[j - 2] && rsi1[j - 2] >= topRsi && rsi1[j - 2] >= rsi1[j]);
            sellConditionMap.put("closeArry[j]>ma20[j]", closeArry[j] > IndicatrixUtils.ma(10, closeArry)[j]);
            sellConditionMap.put("buy!=null", null != buy);


//           sellConditionMap.put("3",(closeArry[j - 3] *1.005>= rsiView.getHighMoreList().get(j-3) && closeArry[j  -2]<= rsiView.getHighMoreList().get(j-2)));
//            sellConditionMap.put("3",(closeArry[j - 1] >= rsiView.getLowClose60() && closeArry[j] < rsiView.getLowClose60() ));
            Boolean sellConditionValue = sellConditionMap.values().stream().allMatch(p -> p);

//            Boolean isCute=false;
////            if(null!=buy && (closeArry[j]-buy)/buy<-0.01){
////                isCute=true;
////            }
//
//            if(null!=maxPrice && null!=buy &&maxPrice>buy && maxPrice>closeArry[j] &&
//                    (maxPrice-buy)/buy >0.05 ){
//                isCute=true;
//            }
            if (sellConditionValue) {
                buy = null;
                maxPrice = null;
                ChartPointView chartPointView = new ChartPointView();
                chartPointView.setName("【S】" + jsonObject.getString("day"));
                chartPointView.setXAxis(jsonObject.getString("day"));
                chartPointView.setYAxis(BaseUtils.stringToBigDecimal(BaseUtils.format(close.doubleValue())));
                int sellRange = 2;
                if (sec == 15) {
                    sellRange = 2 * 4;
                }
                if (j >= (jsonArray.size() - sellRange)) {
                    chartPointView.setValue("SELLNOW");
                } else {
                    chartPointView.setValue("S");
                }
                rsiView.getBuyPointViewList().add(chartPointView);
                rsiView.setDesc(rsiView.getDesc() + "【S】" + rsi1[j - 1] + ",nowRsi:" + rsi1[j] + jsonObject.getString("day") + "<br/>");
                System.out.println("-------【S】" + "[j-2]=" + rsi1[j - 2] + "[j-1]=" + rsi1[j - 1] + ",j=" + rsi1[j] + jsonObject.toJSONString());
//                System.out.println("-------【S】" +"DIFF,="+  rsi1[j-2] +",json="+ jsonObject.toJSONString());
            }
//                if(CollectionUtils.isNotEmpty(chartPointViews)){
//                    rsiView.getBuyPointViewList().add(chartPointViews);
//                }
            if (j == (jsonArray.size() - 1)) {
                System.out.println(topRsi + "【NOW】" + rsi1[j] + jsonObject.toJSONString());
            }

        }

        rsiView.setLowMacdDiff(new BigDecimal(lowMacdDiff));
        rsiView.setTopMacdDiff(new BigDecimal(topMacdDiff));
        return rsiView;
    }

    private double getChp(JSONArray jsonArray, int j) {
        return (jsonArray.getJSONObject(j).getBigDecimal("close").doubleValue() - jsonArray.getJSONObject(j - 1).getBigDecimal("close").doubleValue()) / jsonArray.getJSONObject(j - 1).getBigDecimal("close").doubleValue() * 100;
    }

    private Boolean isLowRange(double[] closeArry, int j, double ma20, int n) {
        for (int i = j - n; i < j; i++) {
            if (closeArry[i] <= ma20) {
                return true;
            }
        }
        return false;
    }

    private Boolean isHightRange(double[] closeArry, int j, double ma20, int n) {
        for (int i = j - n; i < j; i++) {
            if (closeArry[i] >= ma20) {
                return true;
            }
        }
        return false;
    }


    public StockReportRsiView analysisRsi(String code) throws Exception {
        return analysisRsi(code, null, false);
    }

    public StockReportRsiView analysisRsi(String code, AnalysisContext analysisContext, boolean forceRecache) throws Exception {
        if (null == analysisContext) {
            analysisContext = new AnalysisContext();
        }
        String localCacheKey = "analysisRsi_" + code;
        String localCacheValue = GuavaCacheUtils.get(localCacheKey);
        if (StringUtils.isNotEmpty(localCacheValue) && !forceRecache) {
            return JSONObject.parseObject(localCacheValue, StockReportRsiView.class);
        }

        StockReportRsiView view = new StockReportRsiView();
        List<DailyStockData> byCodeList = getByCode(code);
        initName(byCodeList);
        view.setToDay(byCodeList.get(byCodeList.size() - 1));

        List<BigDecimal> volumeList = CollectionUtils.emptyIfNull(byCodeList).stream().filter(
                d -> null != d.getVolume()
        ).map(DailyStockData::getVolume).collect(Collectors.toList());
        double lowVolume = IndicatrixUtils.getPercentileWithBigDecimal(volumeList, 0.4);
        double topVolume = IndicatrixUtils.getPercentileWithBigDecimal(volumeList, 0.6);


        List<BigDecimal> close1List = CollectionUtils.emptyIfNull(byCodeList).stream().filter(
                d -> null != d.getRsi1()
        ).map(DailyStockData::getClose).collect(Collectors.toList());
        double lowClosePercentileWithBigDecimal = IndicatrixUtils.getPercentileWithBigDecimal(close1List, 0.08);

        List<BigDecimal> close20MinList = Lists.newArrayList(close1List);
        List<BigDecimal> close20MaxList = Lists.newArrayList(close1List);


        List<BigDecimal> rsi1List = CollectionUtils.emptyIfNull(byCodeList).stream().filter(
                d -> null != d.getRsi1()
        ).map(DailyStockData::getRsi1).collect(Collectors.toList());
        double lowRsiLine = IndicatrixUtils.getPercentileWithBigDecimal2(rsi1List, 0.095);
        double topRsiLine = IndicatrixUtils.getPercentileWithBigDecimal2(rsi1List, 0.9);


        List<BigDecimal> rs2List = CollectionUtils.emptyIfNull(byCodeList).stream().filter(
                d -> null != d.getRsi2()
        ).map(DailyStockData::getRsi2).collect(Collectors.toList());
        double low2PercentileWithBigDecimal = IndicatrixUtils.getPercentileWithBigDecimal(rs2List, 0.1);
        double top2PercentileWithBigDecimal = IndicatrixUtils.getPercentileWithBigDecimal(rs2List, 0.8);


        List<BigDecimal> rs31List = CollectionUtils.emptyIfNull(byCodeList).stream().filter(
                d -> null != d.getRsi3()
        ).map(DailyStockData::getRsi3).collect(Collectors.toList());
        double low3PercentileWithBigDecimal = IndicatrixUtils.getPercentileWithBigDecimal(rs31List, 0.1);
        double top3PercentileWithBigDecimal = IndicatrixUtils.getPercentileWithBigDecimal(rs31List, 0.8);


        List<Integer> dayList = CollectionUtils.emptyIfNull(byCodeList).stream().filter(
                d -> null != d.getRsi1()
        ).map(DailyStockData::getCurDate).collect(Collectors.toList());
        view.setDayList(dayList);

        List<BigDecimal> rsiList = CollectionUtils.emptyIfNull(byCodeList).stream().filter(
                d -> null != d.getRsi1()
        ).map(DailyStockData::getRsi1).collect(Collectors.toList());
        view.setRsiList(rsiList);

        List<DailyStockData> stockDataList = CollectionUtils.emptyIfNull(byCodeList).stream().filter(
                d -> null != d.getRsi1()
        ).collect(Collectors.toList());

        List<BigDecimal> closeList = CollectionUtils.emptyIfNull(stockDataList).stream().map(DailyStockData::getClose).collect(Collectors.toList());
        view.setCloseList(closeList);

//        double lowClose = IndicatrixUtils.getPercentileWithBigDecimal(closeList, 0.2);
//        view.setLowClose(BaseUtils.stringToBigDecimal(BaseUtils.format(lowClose)).doubleValue());


        List<BigDecimal> ma20List = CollectionUtils.emptyIfNull(stockDataList).stream().map(DailyStockData::getMa20).collect(Collectors.toList());
        view.setMa20List(ma20List);

        List<BigDecimal> ma10List = CollectionUtils.emptyIfNull(stockDataList).stream().map(DailyStockData::getMa10).collect(Collectors.toList());
        view.setMa10List(ma10List);

        List<BigDecimal> ma5List = CollectionUtils.emptyIfNull(stockDataList).stream().map(DailyStockData::getMa5).collect(Collectors.toList());
        view.setMa5List(ma5List);

        double chpsum = 0.00;
        //购买区域
        List<List<ChartPointView>> buyPointViewList = Lists.newArrayList();
        int successCount = 0;
        int cuteCount = 0;

        int startI = 30 + 100;
        List<BigDecimal> chpList = Lists.newArrayList();
        for (int i = 0; i < stockDataList.size(); i++) {
            chpList.add(new BigDecimal(0.00));
        }
        view.setChpList(chpList);

        for (int i = startI; i < stockDataList.size(); i++) {

            DailyStockData cur = stockDataList.get(i);


            close20MinList.set(i, IndicatrixUtils.min(beforeNDay(stockDataList, i, 14).stream().map(d -> d.getLow()).collect(Collectors.toList())));
            close20MaxList.set(i, IndicatrixUtils.max(beforeNDay(stockDataList, i, 30).stream().map(d -> d.getHigh()).collect(Collectors.toList())));
            view.getMa10List().set(i, IndicatrixUtils.min(beforeNDay(stockDataList, i, 7).stream().map(d -> d.getLow()).collect(Collectors.toList())));

            double beforeNDayMinClose20 = beforeNDayMinClose(stockDataList, i, 20);
            view.getMa5List().set(i, new BigDecimal(beforeNDayMinClose20));

            boolean buyLow = buy4(stockDataList, analysisContext, lowRsiLine, lowClosePercentileWithBigDecimal, cur, i);
            boolean buyUp = buy1(stockDataList, analysisContext, lowRsiLine, lowClosePercentileWithBigDecimal, cur, i);
            buyUp = false;
            if (
                    buyLow || buyUp
            ) {
                List<ChartPointView> buyPointView = Lists.newArrayList();
                double maxChp = 0.00;
                for (int j = i; j < stockDataList.size(); j++) {
                    DailyStockData curData = stockDataList.get(j);
                    DailyStockData bf = stockDataList.get(j - 1);
                    double chp = (curData.getClose().doubleValue() - cur.getClose().doubleValue()) / cur.getClose().doubleValue() * 100;
                    if (chp > maxChp) {
                        maxChp = chp;
                    }
                    chpList.set(j, new BigDecimal(chpList.get(j).doubleValue() + chp));


                    boolean backChp = chp < maxChp * 0.8 && maxChp > 25 && chp > 0 && cur.getClose().doubleValue() <= cur.getMa10().doubleValue();
                    boolean backChp2 = chp < maxChp * 0.8 && maxChp > 20 && maxChp < 25 && chp > 0 && cur.getClose().doubleValue() <= cur.getMa10().doubleValue();
                    boolean backChp3 = chp < maxChp * 0.7 && maxChp > 15 && maxChp < 20 && chp > 0 && cur.getClose().doubleValue() <= cur.getMa10().doubleValue();
                    ;
                    boolean backChp4 = chp < maxChp * 0.2 && maxChp > 10 && maxChp < 15 && chp > 0 && cur.getClose().doubleValue() <= cur.getMa10().doubleValue();
                    ;
                    boolean backChp5 = chp < maxChp * 0.2 && maxChp > 5 && maxChp < 10 && chp > 0;


                    boolean backChp7 = cur.getClose().doubleValue() <= beforeNDayMinClose(stockDataList, j, 20) * 1.001 && chp > 0;

                    if (buyUp) {
                        backChp5 = chp < maxChp * 0.8 && maxChp > 10 && chp > 0;
                    }
                    int cuteChp = buyUp ? -5 : -10;//buy3 ? -2 : -8;
                    boolean isStop = backChp || backChp2 || backChp3 || backChp4 || backChp5 || backChp7 || chp < cuteChp;

                    if (
                            isStop || j == stockDataList.size() - 1) {

                        if (chp > 0) {
                            successCount = successCount + 1;
                        } else {
                            cuteCount = cuteCount + 1;
                        }

                        Set<String> reasonSet = Sets.newHashSet();
                        if (buyLow) {
                            reasonSet.add("低买");
                        }
                        if (buyUp) {
                            reasonSet.add("追买");
                        }

                        chpsum = buyRange(chpsum, buyPointViewList, i, cur, buyPointView, j, curData, StringUtils.join(reasonSet, "|"), isStop);
                        break;
                    }
                }
            }
        }
        view.setSuccessCount(successCount);
        view.setCuteCount(cuteCount);
        view.setBuyPointViewList(buyPointViewList);
        view.setClose20MinList(close20MinList);
        view.setClose20MaxList(close20MaxList);

        view.setLow20Rsi(new BigDecimal(lowRsiLine));
        view.setTop80Rsi(new BigDecimal(topRsiLine));
        DecimalFormat df = new DecimalFormat("#.00");
        view.setChpsum(BaseUtils.stringToBigDecimal(df.format(chpsum)).doubleValue());


        double startClose = stockDataList.get(0).getClose().doubleValue();
        double endClose = stockDataList.get(stockDataList.size() - 1).getClose().doubleValue();

        Double rangeChpsum = (endClose - startClose) / startClose * 100;

        view.setRangeChpsum(BaseUtils.stringToBigDecimal(df.format(rangeChpsum)).doubleValue());

        String desc = buildDesc(view);
        view.setDesc(desc);

        GuavaCacheUtils.put(localCacheKey, JSONObject.toJSONString(view));
        return view;

    }

    public static double beforeNDayMinClose(List<DailyStockData> dataList, Integer index, Integer nDay) {

        List<BigDecimal> lowList = Lists.newArrayList();
        for (int i = nDay; i > 0; i--) {
            DailyStockData dailyStockData = dataList.get(index - i);
            lowList.add(dailyStockData.getClose());
        }
        return IndicatrixUtils.min(lowList).doubleValue();

    }

    public static double beforeNDayMinRsi(List<DailyStockData> dataList, Integer index, Integer nDay) {

        List<BigDecimal> lowList = Lists.newArrayList();
        for (int i = nDay; i > 0; i--) {
            DailyStockData dailyStockData = dataList.get(index - i);
            lowList.add(dailyStockData.getRsi1());
        }
        return IndicatrixUtils.min(lowList).doubleValue();

    }

    public static double beforeNDayMaxClose(List<DailyStockData> dataList, Integer index, Integer nDay) {
        if (index >= nDay) {
            List<BigDecimal> hightList = Lists.newArrayList();
            for (int i = nDay; i > 0; i--) {
                DailyStockData dailyStockData = dataList.get(index - i);
                hightList.add(dailyStockData.getHigh());
            }
            return IndicatrixUtils.max(hightList).doubleValue();
        }
        return dataList.get(index).getHigh().doubleValue();
    }

    public static List<DailyStockData> beforeNDay(List<DailyStockData> dataList, Integer index, Integer nDay) {
        List<DailyStockData> list = Lists.newArrayList();
        if (index >= nDay) {
            for (int i = nDay; i > 0; i--) {
                if ((index - i) == 0) {
                    return list;
                }
                DailyStockData dailyStockData = dataList.get(index - i);
                list.add(dailyStockData);
            }
        }
        return list;
    }


    //汇总:平均成功率70.00,平均收益8.61，总操作次数:110,分值=602.64
    private static boolean buy4(List<DailyStockData> stockDataList, AnalysisContext analysisContext, Double lowRsiLine, Double lowCloseLine, DailyStockData cur, Integer index) {

        int mDay = analysisContext.getMDay() != null ? analysisContext.getMDay() : 120;
//        List<DailyStockData> minCloseNDay = beforeNDay(stockDataList, index, 3);
        double minClose = beforeNDayMinClose(stockDataList, index, mDay);

        int shorDay = analysisContext.getShortDay() != null ? analysisContext.getShortDay() : 7;
        List<DailyStockData> beforeNDay = beforeNDay(stockDataList, index, shorDay);
        List<BigDecimal> beforeNDayRsi = beforeNDay.stream().map(d -> d.getRsi1()).collect(Collectors.toList());
        double minRsi = IndicatrixUtils.min(beforeNDayRsi).doubleValue();
        DailyStockData bf = stockDataList.get(index - 1);
        DailyStockData bbf = stockDataList.get(index - 2);

        boolean lowRsi = bbf.getRsi1().doubleValue() <= minRsi * 1.001 || bbf.getRsi1().doubleValue() <= lowRsiLine;

        boolean doubleLow = bbf.getChgp().doubleValue() < -5 && bf.getChgp().doubleValue() < -5 && cur.getChgp().doubleValue() > 0 && lowRsi;


        boolean isOverVolume = cur.getVolume().doubleValue() >= IndicatrixUtils.avg(beforeNDay(stockDataList, index, 20).stream().map(d -> d.getVolume()).collect(Collectors.toList())).doubleValue();
        boolean up = cur.getChgp().doubleValue() > 2;

//        boolean hasMinClose=minCloseNDay.stream().anyMatch(m->m.getClose().doubleValue()<=minClose);

        boolean bbfMinClose = bbf.getClose().doubleValue() <= minClose * 1.001;

        return
                cur.getClose().doubleValue() <= lowCloseLine
                        &&
                        bbfMinClose
                        && cur.getClose().doubleValue() > minClose
                        && cur.getClose().doubleValue() > cur.getMa5().doubleValue()
                        && lowRsi
                        || doubleLow;
    }


    /**
     * //昨天小于lowRsi,当天大于lowRsi,涨幅小于3%,收盘价>ma5
     */
    private static boolean buy1(List<DailyStockData> stockDataList, AnalysisContext analysisContext, Double lowRsiLine, Double lowCloseLine, DailyStockData cur, Integer index) {
        double minClose20 = beforeNDayMinClose(stockDataList, index, 20);
        double minClose10 = beforeNDayMinClose(stockDataList, index, 10);
        boolean minCloseUp = minClose10 > minClose20;

        List<DailyStockData> beforeNDay = beforeNDay(stockDataList, index, 7);
        List<BigDecimal> beforeNDayRsi = beforeNDay.stream().map(d -> d.getRsi1()).collect(Collectors.toList());
        double minRsi = IndicatrixUtils.min(beforeNDayRsi).doubleValue();
        DailyStockData bbf = stockDataList.get(index - 2);
        boolean lowRsi = bbf.getRsi1().doubleValue() <= minRsi * 1.001;

        boolean bbfMinClose = bbf.getClose().doubleValue() <= minClose10 * 1.001;

        return
                minCloseUp
                        && cur.getClose().doubleValue() <= lowCloseLine
                        && bbfMinClose
                        && cur.getClose().doubleValue() > minClose10
                        && cur.getClose().doubleValue() > bbf.getMa5().doubleValue()
                        && lowRsi;

    }

    private static boolean buy3(List<DailyStockData> stockDataList, Double lowPercentileWithBigDecimal, Double lowClosePercentileWithBigDecimal, DailyStockData cur, DailyStockData before, Integer index) {


        List<DailyStockData> beforNDayList = beforeNDay(stockDataList, index, 5);
        List<DailyStockData> overMA10 = beforNDayList.stream().filter(d -> {
            return d.getClose().doubleValue() > d.getMa10().doubleValue();
        }).collect(Collectors.toList());
        if (overMA10.size() > 4 && cur.getClose().doubleValue() > cur.getMa10().doubleValue()) {
            return true;
        }

        return false;

    }

    /**
     * 收盘价>ma5
     *
     * @param lowPercentileWithBigDecimal
     * @param cur
     * @param before
     * @return
     */
    private static boolean buy2(double lowPercentileWithBigDecimal, DailyStockData cur, DailyStockData before) {
        return before.getMa5().doubleValue() < before.getMa10().doubleValue()
                && cur.getMa5().doubleValue() < before.getMa10().doubleValue()
                && cur.getClose().doubleValue() > cur.getMa10().doubleValue()
                && cur.getRsi1().doubleValue() > lowPercentileWithBigDecimal
                && before.getRsi1().doubleValue() < lowPercentileWithBigDecimal
                ;
    }

    private static boolean buy3(double lowPercentileWithBigDecimal, DailyStockData cur, DailyStockData before) {
        return cur.getClose().doubleValue() > before.getMa5().doubleValue()
                && before.getRsi1().doubleValue() < lowPercentileWithBigDecimal
                && cur.getRsi1().doubleValue() > lowPercentileWithBigDecimal
                ;
    }

    private static String buildDesc(StockReportRsiView view) {
        Map<String, BigDecimal> closeMap = Maps.newHashMap();
        for (int i = 0; i < view.getDayList().size(); i++) {
            Integer day = view.getDayList().get(i);
            closeMap.put(day.toString(), view.getCloseList().get(i));
        }
        int successCount = 0;
        int cuteCount = 0;
        String buyRangeDesc = "";
        for (List<ChartPointView> chartPointViews : view.getBuyPointViewList()) {
            String start = chartPointViews.get(0).getXAxis();
            String end = chartPointViews.get(1).getXAxis();
            BigDecimal startClose = closeMap.getOrDefault(start, null);
            BigDecimal endClose = closeMap.getOrDefault(end, null);
            BigDecimal chp = new BigDecimal(0.00);
            chp = new BigDecimal((endClose.doubleValue() - startClose.doubleValue()) / startClose.doubleValue() * 100);
            if (chp.doubleValue() > 0) {
                successCount = successCount + 1;
            } else {
                cuteCount = cuteCount + 1;
            }

            log.info("{}-{}-({}%),策略:{}", start, end, BaseUtils.format(chp.doubleValue()), chartPointViews.get(0).getName());
            buyRangeDesc += String.format("%s-%s-(%s)%%,策略:%s<br/>", start, end, BaseUtils.format(chp.doubleValue()), chartPointViews.get(0).getName());
        }

        double successP = 0;
        if (successCount > 0) {
            successP = (Double.valueOf(successCount) / Double.valueOf((successCount + cuteCount))) * 100;
        }

        view.setBuyRangeDesc(buyRangeDesc);
        String desc = "区间:(%s%%)收益:(%s%%)<br/>成功率(%s%%),成功次数:%s,失败次数:%s";
        return String.format(desc, view.getRangeChpsum(), view.getChpsum(), BaseUtils.format(successP), successCount, cuteCount);
    }

    private static double buyRange(double chpsum, List<List<ChartPointView>> buyPointViewList, int i, DailyStockData cur, List<ChartPointView> buyPointView, int j, DailyStockData af, String reason, boolean isStop) {
        ChartPointView pointView = new ChartPointView();
        double chp = (af.getClose().doubleValue() - cur.getClose().doubleValue()) / cur.getClose().doubleValue() * 100;
        DecimalFormat df = new DecimalFormat("#.00");
        chpsum = chpsum + chp;

        pointView.setName(reason + ",chp=" + df.format(chp) + "%");
        pointView.setXAxis(cur.getCurDate().toString());
        buyPointView.add(pointView);
        ChartPointView pointViewEnd = new ChartPointView();
        pointViewEnd.setXAxis(af.getCurDate().toString());
        if (isStop) {
            pointViewEnd.setName("sell");
        }
        buyPointView.add(pointViewEnd);
        buyPointViewList.add(buyPointView);
        return chpsum;
    }

    public DailyStockData convertStockDay(FinanceHttpApi.HttpStockData day) {
        DailyStockData dailyStockData = new DailyStockData();
        dailyStockData.setStockCode(day.getStockCode());
        dailyStockData.setStockName(StringUtils.defaultString(day.getStockName(), ""));
        dailyStockData.setCurDate(Integer.valueOf(day.getCurDate().replaceAll("\"", "").replaceAll("-", "")));
        dailyStockData.setMoney(day.getClose());
        dailyStockData.setOpen(day.getOpen());
        dailyStockData.setHigh(day.getHigh());
        dailyStockData.setClose(day.getClose());
        dailyStockData.setLow(day.getLow());
        dailyStockData.setVolume(day.getVolume());
        dailyStockData.setChg(day.getChg());
        dailyStockData.setChgp(day.getChgP());
        dailyStockData.setMa5(day.getMa5());
        dailyStockData.setMa10(day.getMa10());
        dailyStockData.setMa20(day.getMa20());
        dailyStockData.setPe(day.getPe());
        dailyStockData.setVolumeMoney(day.getVolumeMoney());
        return dailyStockData;
    }

    public void refreshWg(){
        Set<String> peSet= Sets.newHashSet("510300","159915");
        String result = nocodeDbApi.query("wg", "grid_history", "?select=*&order=created_at.desc&limit=1000");
        JSONArray jsonArray = JSONArray.parseArray(result);
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            String name = jsonObject.getString("name");
            // 正则表达式：匹配连续6位数字
            Pattern pattern = Pattern.compile("\\d{6}");
            Matcher matcher = pattern.matcher(name);
            if(matcher.find()){
                String stockCode = matcher.group(0);
                String stock = stock(stockCode, false);
                BacktestResult backtestResult = runBacktest(stock);
                JSONObject stockJSON = JSONObject.parseObject(stock);
                String atr21 = stockJSON.getString("atr21");
                FinanceHttpApi.HttpStockData  stockInfo=stockJSON.getObject("stock",FinanceHttpApi.HttpStockData.class);
//                etf.atr21/etf.stock.close*100
                BigDecimal closeBigDecimal = stockInfo.getClose();
                double atr21Double = BaseUtils.stringToBigDecimal(atr21).doubleValue() / closeBigDecimal.doubleValue()*100;
                JSONObject updateObject=JSONObject.parseObject("{}");
                updateObject.put("volatility",String.format("%.2f", atr21Double));
                updateObject.put("current_price",stockInfo.getClose());
                updateObject.put("name",stockInfo.getStockName()+"("+stockInfo.getStockCode()+")");
                updateObject.put("rsi",stockInfo.getRsi());

                List<String> tagsList=Lists.newArrayList();
                if(closeBigDecimal.doubleValue()>=stockInfo.getMa14().doubleValue() && closeBigDecimal.doubleValue()>=stockInfo.getMa60().doubleValue()){
                    tagsList.add("14日线上");
                    tagsList.add("建议1倍波动率");
                }else if(closeBigDecimal.doubleValue()<=stockInfo.getMa14().doubleValue() && closeBigDecimal.doubleValue()<=stockInfo.getMa60().doubleValue()){
                    tagsList.add("建议3倍波动率");
                }else{
                    tagsList.add("建议2倍波动率");
                }
                if(backtestResult.isToDayBuy()){
                    tagsList.add("买入");
                }
                if(backtestResult.isToDaySell()){
                    tagsList.add("卖出");
                }
                if(CollectionUtils.isNotEmpty(tagsList)) {
                    updateObject.put("tags", StringUtils.join(tagsList, ","));
                }else{
                    updateObject.put("tags", "空");
                }
                if(stockCode.equals("510300")){
                    String pe300 = FinanceHttpApi.get300Pe();
                    if(StringUtils.isNotEmpty(pe300)){
                        updateObject.put("current_pe",pe300);
                    }
                }
                if(stockCode.equals("510500")){
                    String pe500 = FinanceHttpApi.get500Pe();
                    if(StringUtils.isNotEmpty(pe500)){
                        updateObject.put("current_pe",pe500);
                    }
                }
                if(stockCode.equals("159915")){
                    String peCy = FinanceHttpApi.getCYPe();
                    if(StringUtils.isNotEmpty(peCy)){
                        updateObject.put("current_pe",peCy);
                    }
                }
                Integer id = jsonObject.getInteger("id");
                Boolean update = nocodeDbApi.update("wg", "grid_history", "?id=eq."+id,updateObject.toJSONString());
                System.out.println("更新网格："+update);
            }
        }
    }

//    public Map<String, List<JSONObject>>  hotStock(){
//        try {
//            List<JSONObject> list1 = getFirstN(EastMoneyStockApi.getHotStock(1), 50);
//            List<JSONObject> list2 = getFirstN(EastMoneyStockApi.getHotStock(3), 50);
//            List<JSONObject> list3 = getFirstN(EastMoneyStockApi.getHotStock(5), 50);
//            List<JSONObject> result=Lists.newArrayList();
//            // 2. 统计所有f12值及关联对象
//            Map<String, List<JSONObject>> f12Map = new TreeMap<>();
//            addToMap(f12Map, list1);
//            addToMap(f12Map, list2);
//            addToMap(f12Map, list3);
//
//            list1.stream().map(l->l.get("f12")).;
//            // 3. 筛选在三个集合中都存在的f12值
//            for (Map.Entry<String, List<JSONObject>> entry : f12Map.entrySet()) {
//                String f12 = entry.getKey();
//                List<JSONObject> objects = entry.getValue();
//
//                // 检查是否在三个集合中都出现
//                boolean inList1 = objects.stream().anyMatch(list1::contains);
//                boolean inList2 = objects.stream().anyMatch(list2::contains);
//                boolean inList3 = objects.stream().anyMatch(list3::contains);
//
//                if (inList1 && inList2 && inList3) {
//                    result.add(objects.get())
//                }
//            }
//        } catch (Exception e) {
//
//            e.printStackTrace();;
//        }
//    }

    // 提取前N个元素
    public static List<JSONObject> getFirstN(JSONArray array, int N) {
        List<JSONObject> result = new ArrayList<>();
        int size = Math.min(N, array.size()); // 避免越界
        for (int i = 0; i < size; i++) {
            JSONObject obj = array.getJSONObject(i); // 获取JSONObject
            result.add(obj);
        }
        return result;
    }

    // 将元素按f12值分组存入Map
    private static void addToMap(Map<String, List<JSONObject>> map, List<JSONObject> list) {
        for (JSONObject obj : list) {
            String f12 = obj.getString("f12"); // 获取f12值
            map.computeIfAbsent(f12, k -> new ArrayList<>()).add(obj);
        }
    }
}
